package com.woniuxy.cloud.depositassembly.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniuxy.cloud.api.OrderApi;
import com.woniuxy.cloud.api.UserQueryAPI;
import com.woniuxy.cloud.api.dto.UserDto;
import com.woniuxy.cloud.api.param.BackDepositParam;
import com.woniuxy.cloud.api.param.DepositLevelParam;
import com.woniuxy.cloud.depositassembly.config.AppConfig;
import com.woniuxy.cloud.depositassembly.entity.DepositLevel;
import com.woniuxy.cloud.depositassembly.entity.DepositOrder;
import com.woniuxy.cloud.depositassembly.exception.DepositException;
import com.woniuxy.cloud.depositassembly.mapper.DepositOrderMapper;
import com.woniuxy.cloud.depositassembly.param.OrderParam;
import com.woniuxy.cloud.depositassembly.service.DepositLevelService;
import com.woniuxy.cloud.depositassembly.service.DepositOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniuxy.cloud.depositassembly.util.AliPayUtil;
import com.woniuxy.cloud.depositassembly.util.WoniuBeanUtils;
import com.woniuxy.cloud.api.dto.DepositOrderDto;
import com.woniuxy.cloud.api.param.DepositReduceParam;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 高强
 * @since 2022-02-11
 */
@Service
@Slf4j
public class DepositOrderServiceImpl extends ServiceImpl<DepositOrderMapper, DepositOrder> implements DepositOrderService {

    @Resource
    private RocketMQTemplate rocketMQTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private UserQueryAPI userQueryAPI;

    @Resource
    private DepositLevelService levelService;

    @Resource
    private OrderApi orderApi;

    /**
     * 交押金，创建押金订单
     * @param orderParam
     * @return
     */
    @GlobalTransactional
    @Override
    public DepositOrderDto AddDepositOrder(OrderParam orderParam) {
        //判断押金是不是400的倍数
        Double level = orderParam.getPrice()/400;
        if (level!=1.0&&level!=2.0&&level!=3&&level!=4)throw new DepositException("押金请交400的倍数，最多1600",503);
        //取出userId
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String userId = request.getHeader("userId");
        if (userId==null)throw new DepositException("登录失效",503);

        //先判读用户相关信息
        UserDto user = userQueryAPI.findUserById(Integer.valueOf(userId));
        if (StringUtils.isEmpty(user)) throw new DepositException("该订单修改失败，查无此人", 10702);
        if ( user.getDepositLevel()== 4) throw new DepositException("您已经是最高等级了", 10705);
        if ((Integer.valueOf(level.intValue())+user.getDepositLevel()) > 4) throw new DepositException("支付金额超出了押金的范围", 10705);

        //先判读有无为支付的订单
        List<DepositOrder> list = list(new QueryWrapper<DepositOrder>().eq("order_status", 0).eq("user_id", Integer.valueOf(userId))
        .eq("deal_type",0).eq("deposit_del",0));
        if (list.size() > 0) throw new DepositException("您还有未支付的订单，请先完成订单", 10703);

        //查询押金等级
        DepositLevel deposit_level = levelService.getOne(new QueryWrapper<DepositLevel>().eq("deposit_level", (Integer.valueOf(level.intValue()) + user.getDepositLevel())));
        if (StringUtils.isEmpty(deposit_level)) throw new DepositException("没有该押金等级", 10700);

        DepositOrder depositOrder = new DepositOrder();
        //设置未支付
        depositOrder.setUserId(Integer.valueOf(userId));//用户id
        depositOrder.setPrice(orderParam.getPrice());//金额
        depositOrder.setOrderStatus(0);//创建状态，未付款
        depositOrder.setDealType(0);//押金支付类型
        depositOrder.setDepositDel(0);//为未删除
        depositOrder.setDepositId(deposit_level.getDepositId());//设置押金等级id
        depositOrder.setDepositLevel(deposit_level.getDepositLevel());//设置押金等级

        if (save(depositOrder)) {
            // org/apache/rocketmq/store/config/MessageStoreConfig.java
            //private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
            rocketMQTemplate.syncSend(AppConfig.PRODUCER_GROUP, MessageBuilder.withPayload(depositOrder.getDepositOrderId()).build()
                    , 1000l, 5);//测试为2分钟，实际上为5分钟（9）
            return WoniuBeanUtils.copyObject(depositOrder, DepositOrderDto.class);
        }
        throw new DepositException("订单创建失败", 10706);
    }

    /**
     * 修改订单状态和用户押金情况
     * @param id
     * @return
     */
    @GlobalTransactional
    @Override
    public boolean payDeposit(Integer id) {
        log.info("开始修改账单的状态");
        DepositOrder order = getById(id);
        if (StringUtils.isEmpty(order)) throw new DepositException("该订单不存在", 10700);
        if (order.getOrderStatus() != 0) throw new DepositException("该订单不能支付", 10701);
        order.setOrderStatus(1);//改为支付
        order.setOrderMessage("支付完成");//改备注
        if (updateById(order)) {
            Integer userId = order.getUserId();
            RLock rLock = redissonClient.getLock("updateUserLeve880" + userId);

            try {
                boolean isLocked = rLock.tryLock(3, TimeUnit.SECONDS);
                if (isLocked) {
                    // TODO
                    //支付成功以后修改等级
                    System.out.println(order);
                    DepositLevelParam param = new DepositLevelParam();
                    param.setUserId(order.getUserId());
                    param.setDepositLevel(order.getDepositLevel());
                    param.setDepositId(order.getDepositId());
                    UserDto userDto = userQueryAPI.updateUserDepositLevel(param);
                    System.out.println(userDto);
                    return true;
                }
            } catch (Exception e) {
                rLock.unlock();
            }
        }
        throw new DepositException("订单支付失败", 10706);
    }

    /**
     * 调用支付宝接口
     * @param depositOrderId
     * @return
     */
    @Override
    public String alipayDeposit(Integer depositOrderId) {
        DepositOrder order = getById(depositOrderId);
        if (StringUtils.isEmpty(order)) throw new DepositException("该订单不存在", 10700);
        if (order.getOrderStatus() != 0) throw new DepositException("该订单不能支付", 10701);
        /*******************************************************************/
        //商户订单号，商户网站订单系统中唯一订单号，必填
        //手动改造订单编号
        String out_trade_no = order.getDepositOrderId()+":"+System.currentTimeMillis();
        //付款金额，必填
        String total_amount = order.getPrice().toString();
        //订单名称，必填
        String subject = "押金支付";
        //商品描述，可空
        String body = "欢迎给钱";

        /************           开始调用支付宝接口                *********************/
        String alipay = AliPayUtil.alipay(out_trade_no, total_amount, subject, body);
        log.info("支付宝返回地址：{}",alipay);
        return alipay;
    }


    /**
     * 退押金
     * @param param
     * @return
     */
    @GlobalTransactional
    @Override
    public String depositRefund(BackDepositParam param) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Integer userId = Integer.valueOf(request.getHeader("userId"));

        //判断押金是不是400的倍数
        Double level = param.getPrice()/400;
        if (level!=1.0&&level!=2.0&&level!=3&&level!=4)throw new DepositException("押金退去为400的倍数，最多1600",503);

        //判断本服务可否可以
        DepositOrder depositOrder = getOne(
                new QueryWrapper<DepositOrder>().eq("user_id", userId)
                        .eq("order_status", 0)
        );
        if (!StringUtils.isEmpty(depositOrder))throw new DepositException("你有押金账单没有支付，请等账单过期后在退",10025);

        //调取userOrder的服务
         if (!orderApi.isDepositRefund(userId).getData())throw new DepositException("请先完成您的租赁账单，才能退款哟",10025);

         //查询退取金额是否合格
        UserDto userById = userQueryAPI.findUserById(userId);
        if (StringUtils.isEmpty(userById))throw new DepositException("查人失败",10025);
        if (userById.getDepositLevel()<Integer.valueOf(level.intValue()))throw new DepositException("没有这么押金，您退不了",10025);

        //查询等级
        DepositLevel level1 = levelService.getOne(new QueryWrapper<DepositLevel>().eq("deposit_level", userById.getDepositLevel() - level.intValue()));

        //开始退押金
        DepositOrder order = new DepositOrder();
        order.setUserId(userId);
        order.setPrice(param.getPrice());
        order.setOrderStatus(1);
        order.setDealType(1);//为退押金
        order.setOrderMessage("退押金："+param.getOrderMessage());
        order.setDepositDel(0);
        order.setDepositId(level1.getDepositId());
        order.setDepositLevel(level1.getDepositLevel());
        //生成账单
        if (!save(order))throw new DepositException("修改失败",10025);

        //修改等级
        RLock rLock = redissonClient.getLock("updateUserLeve880" + userId);
        try {
            boolean isLocked = rLock.tryLock(3, TimeUnit.SECONDS);
            if (isLocked) {
                // TODO
                //支付成功以后修改等级
                DepositLevelParam param1 = new DepositLevelParam();
                param1.setUserId(userId);
                param1.setDepositLevel(order.getDepositLevel());
                param1.setDepositId(order.getDepositId());
                log.info("开始修改等级:{}",param1);
                userQueryAPI.updateUserDepositLevel(param1);
            }
        } catch (Exception e) {
            rLock.unlock();
        }
        return "退押金成功";
    }

    /**
     * 系统罚款
     * @param param
     * @return
     */
    @GlobalTransactional
    @Override
    public String penalty(DepositReduceParam param) {
        //判断押金是不是400的倍数
        Double level = param.getPrice()/400;
        if (level!=1.0&&level!=2.0&&level!=3&&level!=4)throw new DepositException("押金退去为400的倍数，最多1600",503);

        //得到用户信息
        UserDto userById = userQueryAPI.findUserById(param.getUserId());
        if (StringUtils.isEmpty(userById))throw new DepositException("查人失败",10025);
        if (userById.getDepositLevel()<Integer.valueOf(level.intValue()))throw new DepositException("没有这么押金，罚不了",10025);

        //查询等级,得到新的
        DepositLevel level1 = levelService.getOne(new QueryWrapper<DepositLevel>().eq("deposit_level", userById.getDepositLevel() - level.intValue()));

        //开始退押金
        DepositOrder order = new DepositOrder();
        order.setUserId(param.getUserId());
        order.setPrice(param.getPrice());
        order.setOrderStatus(1);
        order.setDealType(2);//为罚款
        order.setOrderMessage("罚款："+param.getOrderMessage());
        order.setDepositDel(0);
        order.setDepositId(level1.getDepositId());
        order.setDepositLevel(level1.getDepositLevel());
        //生成账单
        if (!save(order))throw new DepositException("修改失败",10025);

        //修改等级
        RLock rLock = redissonClient.getLock("updateUserLeve880" + param.getUserId());
        try {
            boolean isLocked = rLock.tryLock(3, TimeUnit.SECONDS);
            if (isLocked) {
                // TODO
                //支付成功以后修改等级
                DepositLevelParam pa = new DepositLevelParam();
                pa.setUserId(param.getUserId());
                pa.setDepositLevel(order.getDepositLevel());
                pa.setDepositId(order.getDepositId());
                log.info("开始修改等级:{}",pa);
                userQueryAPI.updateUserDepositLevel(pa);
            }
        } catch (Exception e) {
            rLock.unlock();
        }
        return "罚款成功";
    }


}
